﻿using System;
using System.Web.UI.WebControls;
using System.Collections.Generic;
using System.Linq;
using System.Web.UI;

using IdeaNMR.Shared;
using IdeaNMR.Web.Core;
using System.Text.RegularExpressions;

namespace IdeaNMR.Web
{
    [ValidationProperty("IsValid")]
    public partial class ComponentList : System.Web.UI.UserControl
    {
        public event CommandEventHandler AddCommand; // Add Component Button Click
        public event CommandEventHandler ComponentAdded; // Raise aftr component successfully added
        //public event CommandEventHandler RemoveCommand; // Remove Component Button Click
        public event CommandEventHandler ComponentRemoved; // Fired after component successfully removed
        public event CommandEventHandler SelectedChanged; // Selects different component

        [System.ComponentModel.DefaultValue(false)]
        public bool Editable
        {
            get { return _editable; }
            set { _editable = value; }
        }

        public int CountTotal
        {
            get { return _components.Count(); }
        }

        public List<NamedComponent> Components
        {
            get { return _components; }
            set { this._components = value; }
        }

        /// <summary>
        /// True if all Identifiers in NamedComponents are distinct.
        /// </summary>
        public bool IsValid
        {
            get
            {
                int distinct = this._components.Select(cc => cc.Identifier).Distinct().Count();
                int total = this._components.Count;
                return distinct == total;
            }
        }

        public string TitleCssClass
        {
            get { return this.lblTitle.CssClass; }
            set { this.lblTitle.CssClass = value; }
        }
        public string SubTitleCssClass
        {
            get { return this.lblWSComp.CssClass; }
            set
            {
                this.lblWSComp.CssClass = this.lblCPDComp.CssClass = this.lblScrComp.CssClass = value;
            }
        }
        public string ListContainerCssClass
        {
            get { return this.mainWrapper.CssClass; }
            set { this.mainWrapper.CssClass = value; }
        }
        public string SubListContainerCssClass
        {
            get { return this.lblSelectedWaveShapes.CssClass; }
            set
            {
                this.lblSelectedWaveShapes.CssClass = this.lblSelectedCPDs.CssClass = this.lblSelectedScripts.CssClass = value;
            }
        }
        public string ComponentCssClass
        { get; set; }

        /// <summary>
        /// Guid of currently selected Item. Can be used to access that item directly using Components property.
        /// </summary>
        public Guid Selected
        {
            get { return _selected; }
            private set
            {
                if (this._components.Any(cc => cc.Id == value)) this._selected = value;
            }
        }

        /// <summary>
        /// Add component to the list.
        /// </summary>
        /// <param name="iD">Component Guid, must be unique.</param>
        /// <param name="cI">Component content.</param>
        /// <returns>True if new component added or false if component with given iD is already present in the list.</returns>
        /// <exception cref="System.ArgumentNullException">Thrown when one of the parameters is null.</exception>
        public void AddComponent(AuxComponent aC)
        {
            if (aC == null) throw new ArgumentNullException("aC", "Parameter must not be null.");

            EntityType eT = aC.GetEntityType();
            AuxComponent aCcopy;
            switch (eT)
            {
                case EntityType.WaveShape:
                    aCcopy = new Wave();
                    break;
                case EntityType.Cpd:
                    aCcopy = new Cpd();
                    break;
                case EntityType.Script:
                    aCcopy = new Script();
                    break;
                default:
                    string msg = string.Format("Component List is not designed to accept AuxComponent of type {0}.", eT.ToString());
                    throw new NotImplementedException(msg);
            }

            aC.CopyTo(aCcopy, true);

            // If the list already contains the component with given Id then return
            if (_components.Any(cc => cc.AuxComponent.Id == aC.Id)) return;

            NamedComponent nC = new NamedComponent();
            nC.Id = Guid.NewGuid();
            nC.Identifier = aCcopy.Name.ToString().ToLowerInvariant().Substring(0, aCcopy.Name.Length > 8 ? 8 : aCcopy.Name.Length);
            nC.Identifier = Regex.Replace(nC.Identifier, @"\s", string.Empty);

            nC.AuxComponent = aCcopy;

            _components.Add(nC);

            if (ComponentAdded != null) ComponentAdded(this, new CommandEventArgs(string.Empty, nC));
        }

        #region Page Lifecycle
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            if (!Page.IsPostBack)
            {
                this._components = new List<NamedComponent>();
                ibAddWS.CommandArgument = ComponentType.WaveShape.ToString();
                ibAddCPD.CommandArgument = ComponentType.Cpd.ToString();
                ibAddScr.CommandArgument = ComponentType.Script.ToString();
            }
        }
        protected override void LoadViewState(object savedState)
        {
            base.LoadViewState(savedState);
            #region Parsing ViewState
            if (ViewState["_ComponentList_Editable"] is bool) this._editable = (bool)ViewState["_ComponentList_Editable"];
            if (ViewState["_ComponentList_Selected"] is Guid) this._selected = (Guid)ViewState["_ComponentList_Selected"];
            if (ViewState["_ComponentList_Components"] is List<NamedComponent>) this._components = (List<NamedComponent>)ViewState["_ComponentList_Components"];
            #endregion
            // Controls are recreated in order that the control events were properly handled
            // and PostBack data properly applied to them
            foreach (NamedComponent ci in this._components)
            {
                AddWebControlForListEntry(ci);
                if (ComponentAdded != null) ComponentAdded(this, new CommandEventArgs(string.Empty, ci));
            }
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            if (Page.IsPostBack)
            {
                #region Getting Identifiers
                {
                    foreach (NamedComponent cI in this._components)
                    {
                        TextBox myTB = (TextBox)mainWrapper.FindControl(createTextBoxID(cI.Id));
                        if (myTB != null) cI.Identifier = myTB.Text;
                    }
                }
                #endregion
            }
        }

        protected void Page_PreRender(object sender, EventArgs e)
        {
            ViewState["_ComponentList_Editable"] = this._editable;
            ViewState["_ComponentList_Selected"] = this._selected;
            ViewState["_ComponentList_Components"] = this._components;

            ibAddCPD.Visible = ibAddScr.Visible = ibAddWS.Visible = _editable;
            // Redraw controls with changes
            lblSelectedWaveShapes.Controls.Clear();
            lblSelectedCPDs.Controls.Clear();
            lblSelectedScripts.Controls.Clear();
            foreach (NamedComponent ci in this._components)
            {
                AddWebControlForListEntry(ci);
                if (ComponentAdded != null) ComponentAdded(this, new CommandEventArgs(string.Empty, ci));
            }

        }
        #endregion Page Lifecycle

        #region Event Handlers
        protected void lbAdd_Command(object sender, CommandEventArgs e)
        {
            if (AddCommand != null) AddCommand(this, e);
        }

        /// <summary>
        /// Removes the previously selected component from both Dictionary and Web Control
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RemoveComponent(object sender, CommandEventArgs e)
        {
            Guid cKey = new Guid(e.CommandArgument.ToString());
            NamedComponent nC = this._components.Where(cc => cc.Id == cKey).Single();

            _components.Remove(nC);
            Panel r = mainWrapper.FindControl("pan" + cKey.ToString()) as Panel;

            if (r != null) r.Parent.Controls.Remove(r);

            if (ComponentRemoved != null) ComponentRemoved(this, e);
        }

        private void lbComponent_Command(object sender, CommandEventArgs e)
        {
            Guid x = new Guid(e.CommandArgument.ToString());
            Selected = x;
            if (SelectedChanged != null) SelectedChanged(this, e);
        }
        #endregion Event Handlers

        #region Private Members
        private List<NamedComponent> _components; // ViewState["_ComponentList_Components"];
        private bool _editable; // ViewState["_ComponentList_Editable"]
        private Guid _selected; // ViewState["_ComponentList_Selected"]

        /// <summary>
        /// Adds WebControls for a component
        /// </summary>
        /// <param name="cI"></param>
        /// <exception cref="System.ArgumentNullException">Thrown when cI is null.</exception>
        private void AddWebControlForListEntry(NamedComponent cI)
        {
            if (cI == null) throw new ArgumentNullException("cI", "Parameter must not be null");

            Label Elem;	//Label Control Where the components entries will be added as childs
            switch (cI.AuxComponent.GetEntityType())
            {
                case EntityType.WaveShape:
                    Elem = lblSelectedWaveShapes;
                    break;
                case EntityType.Cpd:
                    Elem = lblSelectedCPDs;
                    break;
                case EntityType.Script:
                    Elem = lblSelectedScripts;
                    break;
                default:
                    throw new NotImplementedException("Add Component Control: default Key is bad");
            }
            Elem.Visible = true;

            Panel p = new Panel();
            p.ID = "pan" + cI.Id.ToString();
            p.CssClass = ComponentCssClass;
            Elem.Controls.Add(p);

            if (Editable)
            {
                TextBox tb = new TextBox();
                tb.ID = createTextBoxID(cI.Id);
                tb.CssClass = ComponentCssClass;
                tb.Text = cI.Identifier;
                p.Controls.Add(tb);
            }
            else
            {
                Label lblp = new Label();
                lblp.CssClass = ComponentCssClass;
                lblp.Text = cI.Identifier;
                p.Controls.Add(lblp);
            }

            LinkButton lb = new LinkButton();
            lb.CssClass = ComponentCssClass;
            lb.ID = "lb" + cI.Id.ToString();
            lb.Text = string.Format("<b>{0}</b>", cI.AuxComponent.Name);
            lb.CausesValidation = false;
            lb.Command += new CommandEventHandler(lbComponent_Command);
            lb.CommandName = cI.AuxComponent.GetEntityType().ToString();
            lb.CommandArgument = cI.Id.ToString();
            p.Controls.Add(lb);

            //LiteralControl lc = new LiteralControl();
            //lc.Text = string.Format("<br /><span class=\"{1}\">ID: {{{0}}}</span>", cI.Id, ComponentCssClass);
            //p.Controls.Add(lc);

            if (Editable)
            {
                //New Image Button 
                ImageButton b = new ImageButton();
                b.AlternateText = "Remove";
                b.ID = "ib" + cI.Id.ToString();
                b.CssClass = "componentList_RemoveButton";
                b.ImageUrl = @"Images/Delete.png";
                b.CausesValidation = false;
                b.CommandArgument = cI.Id.ToString();
                b.CommandName = cI.AuxComponent.GetEntityType().ToString();
                b.Command += new CommandEventHandler(RemoveComponent);
                p.Controls.Add(b);
            }
        }

        private string createTextBoxID(Guid guid)
        {
            return string.Format("tb_{0}", guid.ToString());
        }
        #endregion Private Members
    }
}